home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Games / othello / Source / Othello.m < prev    next >
Text File  |  1992-09-27  |  8KB  |  332 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "Othello.h"
  5.  
  6. @implementation Othello
  7.  
  8. - saveGame:sender
  9. {
  10.   NXStream *theStream;
  11.   
  12.   if (!savePanel) savePanel = [SavePanel new];
  13.   
  14.   if ([savePanel runModal]) {
  15.     theStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  16.     NXWrite(theStream, theField, NUM_SPACES * sizeof(short));
  17.     NXWrite(theStream, &theTurn, sizeof(short));
  18.     NXFlush(theStream);
  19.     NXSaveToFile(theStream, [savePanel filename]);
  20.     NXClose(theStream);
  21.   }
  22.   
  23.   return self;
  24. }
  25.  
  26. - openGame:sender
  27. {
  28.   NXStream *theStream;
  29.   static const char *const othType[2] = {"oth", NULL};
  30.   id openPanel = [[OpenPanel new] allowMultipleFiles:NO];
  31.   
  32.   if ([openPanel runModalForTypes:othType]) {
  33.     theStream = NXMapFile([openPanel filename], NX_READONLY);
  34.     NXRead(theStream, theField, NUM_SPACES * sizeof(short));
  35.     NXRead(theStream, &theTurn, sizeof(short));
  36.     NXClose(theStream);
  37.     [self updateField:self];
  38.   }
  39.   return self;
  40. }
  41.  
  42. int count_pieces(theField, thePlayer)
  43.      short *theField, thePlayer;
  44. {
  45.   int i, count=0;
  46.   
  47.   for(i=0; i<NUM_SPACES; i++) {
  48.     if(theField[i]==thePlayer) count++;
  49.   }
  50.   
  51.   return count;
  52. }
  53.  
  54. - newGame:sender
  55. {
  56.   bzero(&theField, NUM_SPACES * sizeof(short));
  57.   
  58.   /* this needs to be computed-- algorithm to find the middle for squares
  59.    * and place the appropriate pieces in those squares.
  60.    */
  61.   theField[27]= WHITE;
  62.   theField[28]= BLACK;
  63.   theField[35]= BLACK;
  64.   theField[36]= WHITE;
  65.   
  66.   [self updateField:self];
  67.   theTurn = WHITE;
  68.   theField[NUM_SPACES]=theTurn;
  69.   [status setStringValue:"White's turn."];
  70.   [white_number setIntValue:count_pieces(theField, WHITE)];
  71.   [black_number setIntValue:count_pieces(theField, BLACK)];
  72.   
  73.   return self;
  74. }
  75.  
  76.  
  77. - updatePlace:(int) theCell forPlayer:(int) thePlayer
  78. {
  79.   switch(thePlayer) {
  80.  case WHITE:
  81.     [[playField findCellWithTag:theCell] setIcon:"White_piece"];
  82.     break;
  83.  case BLACK:
  84.     [[playField findCellWithTag:theCell] setIcon:"Black_piece"];
  85.     break;
  86.  default:
  87.     [[playField findCellWithTag:theCell] setIcon:""];
  88.   }
  89.   
  90.   return self;
  91. }
  92.  
  93. - updateField:sender
  94. {
  95.   int i;
  96.   
  97.   for(i=0; i<NUM_SPACES; i++) {
  98.     switch(theField[i]) {
  99.   case WHITE:
  100.       [[playField findCellWithTag:i] setIcon:"White_piece"];
  101.       break;
  102.   case BLACK:
  103.       [[playField findCellWithTag:i] setIcon:"Black_piece"];
  104.       break;
  105.   default:
  106.       [[playField findCellWithTag:i] setIcon:""];
  107.     }
  108.   }
  109.   
  110.   return self;
  111. }
  112.  
  113.  
  114. int opp(player)
  115.      int player;
  116. {
  117.   if (player == WHITE) {
  118.     return BLACK;
  119.   }
  120.   return WHITE;
  121. }
  122.  
  123. - (BOOL) checkDirection:(int) xDelta:(int) yDelta fromCell:(int)theCell
  124.  forPlayer:(int) thePlayer
  125. {
  126.   int x = x(theCell)+xDelta;
  127.   int y = y(theCell)+yDelta;
  128.   int deltaCell = cell(x, y);
  129.   
  130.   if(x < 0 || x > FIELD_WIDTH-1 || y < 0 || y > FIELD_HEIGHT-1) {
  131.     return NO;
  132.   }
  133.   
  134.   while(theField[deltaCell] == opp(thePlayer)) {
  135.     x = x(deltaCell)+xDelta;
  136.     y = y(deltaCell)+yDelta;
  137.     if(x < 0 || x > FIELD_WIDTH-1 || y < 0 || y > FIELD_HEIGHT-1) {
  138.       return NO;
  139.     }
  140.     deltaCell=cell(x, y);
  141.   }
  142.   
  143.   if (theField[deltaCell]==thePlayer) {
  144.     return YES;
  145.   }
  146.   return NO;
  147. }
  148.  
  149. - (void) doDirection:(int) xDelta:(int) yDelta fromCell:(int)theCell
  150.  forPlayer:(int) thePlayer
  151. {
  152.   int deltaCell=cell((x(theCell)+xDelta), (y(theCell)+yDelta));
  153.   
  154.   while(theField[deltaCell] == opp(thePlayer)) {
  155.     theField[deltaCell] = thePlayer;
  156.     [self updatePlace:deltaCell forPlayer:thePlayer];
  157.     
  158.     deltaCell=cell((x(deltaCell)+xDelta), (y(deltaCell)+yDelta));
  159.   }
  160. }
  161.  
  162. - (BOOL) allDirectionsFromCell:(int)theCell flipPieces:(BOOL) move
  163.  forPlayer:(int) thePlayer
  164. {
  165.   int xDelta, yDelta, x, y;
  166.   BOOL validMove=NO;
  167.   
  168.   for (yDelta=-1; yDelta<2; yDelta++) {
  169.     y = y(theCell) + yDelta;
  170.     if (y < 0 || y > FIELD_HEIGHT-1) continue; /* off board */
  171.     for (xDelta=-1; xDelta<2; xDelta++) {
  172.       x = x(theCell) + xDelta;
  173.       if (x < 0 || x > FIELD_WIDTH-1) continue; /* off board */
  174.       if (theField[cell(x, y)] != opp(thePlayer)) continue; /* not flanked
  175.                                    by opponent */
  176.       /* there is an ajacent opponent this direction-- check to see if there
  177.        * is a flank and make the changes if there is.  the eight directions
  178.        * that need to be changed are independent-- we can safely make the
  179.        * board changes w/o having to worry about an affect on the other
  180.        * directions.
  181.        */
  182.       if ([self checkDirection:xDelta:yDelta fromCell:theCell
  183.        forPlayer:thePlayer]) {
  184.     if (move) [self doDirection:xDelta:yDelta fromCell:theCell
  185.            forPlayer:thePlayer];
  186.     validMove=YES;
  187.       }
  188.     }
  189.   }
  190.   
  191.   return validMove;
  192. }
  193.  
  194. - (BOOL) allDirectionsFromCell:(int)theCell flipPieces:(BOOL) move
  195. {
  196.   return
  197.       [self allDirectionsFromCell:theCell flipPieces:move forPlayer:theTurn];
  198. }
  199.  
  200.  
  201. - (BOOL) canPlay:(int) thePlayer
  202. {
  203.   int i;
  204.   
  205.   /* if there is any move at all, immediately return YES. */
  206.   for(i=0; i<NUM_SPACES; i++) {
  207.     if(theField[i] == EMPTY)
  208.     if([self allDirectionsFromCell:i flipPieces:NO
  209.     forPlayer:thePlayer]) {
  210.       return YES;
  211.     }
  212.   }
  213.   /* otherwise, return NO. */
  214.   return NO;
  215. }
  216.  
  217.  
  218. - (char *) playerToString:(int) thePlayer
  219. {
  220.   char *s;
  221.   s = malloc(6*sizeof(char));
  222.   if (thePlayer == WHITE) {
  223.     strcpy(s, "White");
  224.   } else strcpy(s, "Black");
  225.   return s;
  226. }
  227.  
  228. - (char *) turnString
  229. {
  230.   char *s;
  231.   
  232.   s = malloc(15*sizeof(char));
  233.   strcpy(s, [self playerToString:theTurn]);
  234.   strcat(s, "'s turn.");
  235.   return s;
  236. }
  237.  
  238. - moveMade:sender
  239. {
  240.   char msg[25];
  241.   int theCell;
  242.   int white,black;
  243.   
  244.   if ((theCell = [sender selectedTag]) < 0)
  245.       return self;/* invalid click trap */
  246.   
  247.   if (theField[theCell]) return self; /* bad move -- already occupied */
  248.   
  249.   /* try the move */
  250.   if([self allDirectionsFromCell:theCell flipPieces:NO]) {
  251.     [self backup];
  252.     [self allDirectionsFromCell:theCell flipPieces:YES];
  253.     theField[theCell] = theTurn; /* place the piece */
  254.     [self updatePlace: theCell forPlayer:theTurn];
  255.     
  256.     /* check to see if the next player has a valid move available
  257.      * this could be combined w/the above but would dog out the board
  258.      * refresh and wouldn't be aesthetically pleasing
  259.      */
  260.     
  261.     if([self canPlay:opp(theTurn)]) {
  262.       theTurn=opp(theTurn);
  263.       theField[NUM_SPACES]=theTurn;
  264.       sprintf(msg, "%s's turn.", [self playerToString:theTurn]);
  265.     } else {
  266.       if(![self canPlay:theTurn]) {
  267.     white=count_pieces(theField, WHITE);
  268.     black=count_pieces(theField, BLACK);
  269.     if (black > white) {
  270.       sprintf(msg, "Black wins by %d.", black - white);
  271.     } else
  272.         if (white > black) {
  273.           sprintf(msg, "White wins by %d.", white - black);
  274.         } else sprintf(msg, "Tie game.");
  275.       } else
  276.       sprintf(msg, "%s has no move.  %s's turn.",
  277.           [self playerToString:opp(theTurn)],
  278.           [self playerToString:theTurn]);
  279.     }
  280.   } else sprintf(msg, "Invalid move. %s's turn.",
  281.          [self playerToString:theTurn]);
  282.   
  283.   [white_number setIntValue:count_pieces(theField, WHITE)];
  284.   [black_number setIntValue:count_pieces(theField, BLACK)];
  285.   
  286.   [status setStringValue:msg];
  287.   
  288.   return self;
  289. }
  290.  
  291. - undo:sender
  292. {
  293.   [self swap];
  294.   
  295.   return self;
  296. }
  297.  
  298. - swap
  299. {
  300.   short temp[NUM_SPACES+1];
  301.   char msg[20];
  302.   
  303.   
  304.   bcopy(theBckField, temp, (NUM_SPACES+1) * sizeof(short));
  305.   bcopy(theField, theBckField, (NUM_SPACES+1) * sizeof(short));
  306.   bcopy(temp, theField, (NUM_SPACES+1) * sizeof(short));
  307.   
  308.   [self updateField:self];
  309.   
  310.   theTurn=theField[NUM_SPACES];
  311.   
  312.   sprintf(msg, "%s", [self turnString]);
  313.   [status setStringValue:msg];
  314.   
  315.   return self;
  316. }
  317.  
  318. - backup
  319. {
  320.   bcopy(theField, theBckField, NUM_SPACES * sizeof(short));
  321.   theBckField[NUM_SPACES] = theTurn;
  322.   
  323.   return self;
  324. }
  325.  
  326. - appDidInit:sender
  327. {
  328.   [self newGame:self];
  329.   return self;
  330. }
  331. @end
  332.